﻿namespace PugLib
{
	using System;
	using System.Diagnostics.CodeAnalysis;
	using System.Threading.Tasks;

	/// <summary>
	/// 
	/// </summary>
	[SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes", Justification = "TContinuationState instead of Object")]
	public class AsyncContinuation<TInput, TResult, TContinuationState>
	{
		private readonly Func<TInput, TResult> _action;
		private readonly TInput _arg1;
		private readonly Action<TContinuationState, TResult> _continuation;
		private readonly TContinuationState _continuationState;
		private Task<TResult> _task;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="action"></param>
		/// <param name="arg1"></param>
		/// <param name="continuation"></param>
		/// <param name="continuationState"></param>
		public AsyncContinuation(Func<TInput, TResult> action, TInput arg1,
		                         Action<TContinuationState, TResult> continuation, TContinuationState continuationState)
		{
			_action = action;
			_arg1 = arg1;
			_continuation = continuation;
			_continuationState = continuationState;
		}

		/// <summary>
		/// Starts the action.
		/// </summary>
		/// <returns></returns>
		public IAsyncResult Begin()
		{
			_task = Task.Factory.StartNew(() => _action(_arg1));
			if (_continuation != null)
			{
				_task.ContinueWith(r =>
				                   {
				                   	try
				                   	{
				                   		_continuation(_continuationState, r.Result);
				                   	}
				                   	finally
				                   	{
				                   		_task.Dispose();
				                   		_task = null;
				                   	}
				                   });
			}
			return _task;
		}

		/// <summary>
		/// Completes the action.  Calls the continuation in-line if requested.
		/// </summary>
		/// <param name="result"></param>
		/// <returns>The result</returns>
		public TResult End(IAsyncResult result)
		{
			try
			{
				if (_continuation != null)
				{
					// The callback has been specified; it is violation
					// of the async pattern to use wait handle for completion
					throw new InvalidOperationException(Strings.AsyncResultNoWaitHandleWhenCallback);
				}

				if (result == null)
				{
					throw new ArgumentNullException("result");
				}
				if (result != _task)
				{
					throw new ArgumentException("result does not match this instance");
				}

				TResult taskResult = _task.Result;

				if (_continuation != null)
				{
					_continuation(_continuationState, taskResult);
				}

				return taskResult;
			}
			finally
			{
				if (_task != null)
				{
					_task.Dispose();
					_task = null;
				}
			}
		}
	}

	/// <summary>
	/// 
	/// </summary>
	/// <typeparam name="TInput"></typeparam>
	/// <typeparam name="TResult"></typeparam>
	public sealed class AsyncContinuation<TInput, TResult> : AsyncContinuation<TInput, TResult, Object>
	{
		/// <summary>
		/// 
		/// </summary>
		/// <param name="action"></param>
		/// <param name="arg1"></param>
		public AsyncContinuation(Func<TInput, TResult> action, TInput arg1)
			: base(action, arg1, null, null)
		{
		}
	}
}